package cc.autoapi.pucong.auto.flow.provider.log.manager;

import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.LogLevelTypeEnum;
import cc.autoapi.pucong.auto.flow.core.root.utils.ThreadPoolUtils;
import cc.autoapi.pucong.auto.flow.persistence.bo.log.LogInsertDto;
import cc.autoapi.pucong.auto.flow.persistence.repository.log.ApiInstanceNodeService;
import cc.autoapi.pucong.auto.flow.persistence.repository.log.ApiInstanceService;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;


/**
 * 日志缓存池写入文件 线程处理
 *
 * @author pucong
 * @url https://gitee.com/pucong
 * @create 2023-05-31
 */
@Slf4j
@Service
public class LogManager {

    @Resource
    private ApiInstanceService apiInstanceService;

    @Resource
    private ApiInstanceNodeService apiInstanceNodeService;

    private LinkedBlockingQueue<LogInsertDto> logQueueList = new LinkedBlockingQueue<>();
    private HashMap<String, LogInsertDto> apiLogHashMap = new HashMap<>(16);
    private HashMap<String, LogInsertDto> nodeLogHashMap = new HashMap<>(32);

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    public void initLogDeal() {
        ThreadPoolUtils.executor(() -> {
            // 记录上次执行数据库插入时间
            Date lastExecTime = new Date();
            while (true) {
                try {
                    // 如果时间差大于3s，则执行一次
                    if (DateUtil.between(lastExecTime, new Date(), DateUnit.SECOND) > 3) {
                        // 插入到api日志表
                        append2DbApi();
                        // 插入到node日志表
                        append2DbNode();
                        lastExecTime = new Date();
                    }
                    // 插入到缓存队列
                    LogInsertDto insertDto = logQueueList.poll(1L, TimeUnit.SECONDS);
                    if (insertDto != null) {
                        String apiInstanceId = insertDto.getApiInstanceId();
                        String apiNodeInstanceId = insertDto.getApiNodeInstanceId();
                        if (StringUtils.isNoneBlank(apiInstanceId)) {
                            LogInsertDto apiLogInsertDto = apiLogHashMap.get(apiInstanceId);
                            if (apiLogInsertDto != null) {
                                String msg = apiLogInsertDto.getMsg();
                                String logContent = msg + "\n" + insertDto.getMsg();
                                apiLogInsertDto.setMsg(logContent);
                            } else {
                                apiLogHashMap.put(apiInstanceId, insertDto);
                            }
                        }
                        if (StringUtils.isNoneBlank(apiInstanceId) && StringUtils.isNoneBlank(apiNodeInstanceId)) {
                            String key = apiInstanceId + "_" + apiNodeInstanceId;
                            LogInsertDto apiLogInsertDto = nodeLogHashMap.get(key);
                            if (apiLogInsertDto != null) {
                                String msg = apiLogInsertDto.getMsg();
                                String logContent = msg + "\n" + insertDto.getMsg();
                                apiLogInsertDto.setMsg(logContent);
                            } else {
                                nodeLogHashMap.put(key, insertDto);
                            }
                        }
                    }
                    // 延迟500ms
                    Thread.sleep(500);
                } catch (Exception var12) {
                    log.error("sleep error", var12);
                }
            }
        });
    }

    private void append2DbApi() {
        Set<String> keys = apiLogHashMap.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            LogInsertDto insertDto = apiLogHashMap.get(key);
            if (insertDto != null) {
                String apiInstanceId = insertDto.getApiInstanceId();
                String logContent = insertDto.getMsg();
                if (StringUtils.isNoneBlank(apiInstanceId)) {
                    apiInstanceService.appendApiLog(apiInstanceId, logContent);
                }
                iterator.remove();
            }
        }
    }

    private void append2DbNode() {
        Set<String> keys = nodeLogHashMap.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            LogInsertDto insertDto = nodeLogHashMap.get(key);
            if (insertDto != null) {
                String apiInstanceId = insertDto.getApiInstanceId();
                String apiNodeInstanceId = insertDto.getApiNodeInstanceId();
                String logContent = insertDto.getMsg();
                if (StringUtils.isNoneBlank(apiInstanceId) && StringUtils.isNoneBlank(apiNodeInstanceId)) {
                    apiInstanceNodeService.appendApiNodeLog(apiInstanceId, apiNodeInstanceId, logContent);
                }
                iterator.remove();
            }
        }
    }

    public void appendLog(LogLevelTypeEnum levelTypeEnum, String apiInstanceId, String msg) {
        this.appendLog(levelTypeEnum, apiInstanceId, null, msg);
    }

    public void appendLog(LogLevelTypeEnum levelTypeEnum, String apiInstanceId, String apiNodeInstanceId, String msg) {
        String logContent = "";
        if (StringUtils.isNotBlank(apiNodeInstanceId)) {
            logContent = String.format("%s %s:%s:%s", sdf.format(new Date()), levelTypeEnum.getName(), apiNodeInstanceId, msg);
        } else {
            logContent = String.format("%s %s:%s", sdf.format(new Date()), levelTypeEnum.getName(), msg);
        }
        LogInsertDto add = new LogInsertDto();
        add.setApiInstanceId(apiInstanceId);
        add.setApiNodeInstanceId(apiNodeInstanceId);
        add.setMsg(logContent);
        logQueueList.offer(add);
    }
}
